#include "core.h"
#include "tlbCMP.h"

static address_t nil;


Core::Core(TLBCMP &parent,uint32_t lineSize,uint32_t pageSize,uint32_t TLBSize, uint32_t TLBNways):
    _parent(parent)
{
    _pageSize=pageSize;
    _lineSize=lineSize;
    _TLBSize=TLBSize;
    _TLBAssocBits=_TLBSetBits=0;

    _TLB =  new CacheWriteBack<address_t> (TLBSize,TLBNways,_pageSize,false,1);
    /* L1 TLB cache stats */
    _TLBHits=_TLBHitInvalidate=_TLBMisses=0;
    _TLBReplacements=_TLBEvictions=_TLBAccesses=0;
    _TLBForcedEvictions=0;
    _threadid=_appid=0;
    pthread_mutex_init(&_mutexTLB,NULL);

}

Core::~Core()
{
    delete _TLB;
}

void
Core::reset()
{
    _TLBHits=_TLBHitInvalidate=_TLBMisses=0;
    _TLBReplacements=_TLBEvictions=_TLBAccesses=0;
    _TLBForcedEvictions=0;
    _threadid=_appid=0;
}
void
Core::setTrace(Trace *t,uint32_t appid,uint32_t thread)
{
    _threadid=thread;
    _appid=appid;
    _trace=t;
}

Trace *
Core::getTrace(void)
{
    return _trace;
}

void
Core::getTraceName(char *dirname)
{
    return _trace->getDirname(dirname);
}

void *
Core::execute(void * core_instance)
{
    uint64_t addr=0;
    uint32_t opcode=0;
    uint32_t i=0;

    Core * _c = static_cast<Core *>(core_instance);
    Trace * trace = _c->getTrace();

    std::cout << "application is :"<<_c->getApp()<<std::endl;
    while (trace->getMemoryAccess(_c->getThreadId(),i,addr,opcode)) {
           _c->access(addr);
           i++;
    }

    pthread_exit(NULL);
}

void
Core::flushPage(address_t addr)
{
    // DiDi can force a core TLB to flush a certain entry
    pthread_mutex_lock(&_mutexTLB);
    if (_TLB->flush(addr))
        _TLBForcedEvictions++;
    pthread_mutex_unlock(&_mutexTLB);
}

/* non-optimal version of TLBCMP search */
void
Core::access(address_t addr)
{
    address_t evictPage;
    CacheAccess accessResponse;
    Directory dirEntry(1);
    Directory dirEvict(1);

    pthread_mutex_lock(&_mutexTLB);
    _TLBAccesses++;
    // HIT TLB
    accessResponse=_TLB->read2(addr,evictPage,dirEvict,dirEntry,nil,_threadid);
    if (CacheAccessIsHit(accessResponse)) {
        _TLBHits++;
        pthread_mutex_unlock(&_mutexTLB);
        return;
    }
    _TLBMisses++;
    // On TLB-1 Miss, the page is requested, so stored in L1-L2 (update dir)
    if (CacheAccessIsHitInvalidate(accessResponse)) {
        _TLBHitInvalidate++;
    }
    if (CacheAccessIsEvict(accessResponse)) {
        if (evictPage!=0) {
            _TLBEvictions++;
            // evict only if single directory entry
            pthread_mutex_unlock(&_mutexTLB);
            _parent.evictDiDi(_appid,evictPage,_threadid);
            _parent.addEntryDiDi(_appid,addr,_threadid);
            return;
        }
    }
    pthread_mutex_unlock(&_mutexTLB);
    _parent.addEntryDiDi(_appid,addr,_threadid);
}
